package com.hiss.basic.starter.websocket.listener;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.hiss.basic.starter.websocket.manager.WebSocketSessionManager;
import com.hiss.basic.starter.websocket.model.MessageModel;
import com.hiss.basic.starter.websocket.model.PrincipalModel;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.connection.Message;
import org.springframework.data.redis.connection.MessageListener;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;

import java.util.Map;
import java.util.Set;

@Slf4j
public class WebSocketMessageListener implements MessageListener {

    private static final ObjectMapper objectMapper;

    static {
        objectMapper = new ObjectMapper();
        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
        objectMapper.activateDefaultTyping(objectMapper.getPolymorphicTypeValidator(), ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
        objectMapper.registerModule(new JavaTimeModule());
    }

    @Override
    public void onMessage(Message message, byte[] bytes) {
        log.info("WebSocket 收到消息={}", new String(message.getBody()));

        MessageModel model;
        try {
            model = objectMapper.readValue(new String(message.getBody()), MessageModel.class);
        } catch (Exception e) {
            log.error("WebSocket message listener error", e);
            return;
        }

        // 广播
        if (ObjectUtil.equal(MessageModel.MessageType.BROADCAST, model.getType())) {
            WebSocketSessionManager.getSessions().forEach(session -> {
                sendMessage(session, model.getPayload());
            });
        }
        // 用户
        else if (ObjectUtil.equal(MessageModel.MessageType.USER, model.getType())) {
            Map<String, Set<String>> receivers = model.getReceivers();
            if (CollectionUtil.isNotEmpty(receivers)) {
                receivers.forEach((key, value) -> {
                    value.forEach(receiver -> WebSocketSessionManager.getUserSessions(receiver).forEach(session -> {
                        sendMessage(session, model.getPayload());
                    }));
                });
            }
        }
        // 渠道
        else if (ObjectUtil.equal(MessageModel.MessageType.CHANNEL, model.getType())) {
            Map<String, Set<String>> receivers = model.getReceivers();
            if (CollectionUtil.isNotEmpty(receivers)) {
                receivers.forEach((key, value) -> {
                    value.forEach(receiver -> WebSocketSessionManager.getChannelSessions(receiver).forEach(session -> {
                        sendMessage(session, model.getPayload());
                    }));
                });
            }
        }
        // 渠道&用户
        else if (ObjectUtil.equal(MessageModel.MessageType.CHANNEL_USER, model.getType())) {
            Map<String, Set<String>> receivers = model.getReceivers();
            if (CollectionUtil.isNotEmpty(receivers)) {
                receivers.forEach((key, value) -> {
                    WebSocketSessionManager.getChannelSessions(key).forEach(session -> {
                        PrincipalModel principal = WebSocketSessionManager.getPrincipalModel(session);
                        if (ObjectUtil.isNotNull(principal)) {
                            if (value.contains(principal.getUserId())) {
                                sendMessage(session, model.getPayload());
                            }
                        }
                    });
                });
            }
        }
    }

    private static void sendMessage(WebSocketSession session, String payload) {
        try {
            if (ObjectUtil.isNotNull(session) && session.isOpen()) {
                session.sendMessage(new TextMessage(payload));
            }
        } catch (Exception e) {
            log.error("WebSocket 消息发送失败", e);
        }
    }
}
